The approaches described in this section are approaches that could be pursued, but not necessarily approaches that have been previously conceived or pursued. Therefore, unless otherwise indicated, it should not be assumed that any of the approaches described in this section qualify as prior art merely by virtue of their inclusion in this section.
Many documents may be represented as data objects that include one or more nodes. Further, the nodes of a data object may be organized according some logical representation (e.g. an ordered or a linked list, a tree, or a graph). For example, a properly formatted extensible Markup Language (XML) document may be represented by a data object in which the nodes (e.g. the XML elements of the XML document) are organized in a hierarchical tree.
Throughout the lifetime of a data object, different versions of the object may be created and stored for various purposes. One implementation of whole-object versioning semantics is to make a complete copy of the entire data object with each new version, then apply the new changes to the copy. (A complete copy of an entire data object, including all nodes thereof, is referred to herein as a “deep copy” of the data object.) This “deep copy” implementation of object versioning supports the creation of “in-between” versions—that is, a user can go back to an older version of the data object, make some edits, and save those changes in the older version without affecting later versions of the object. This implementation of object versioning is an efficient way to support versioned reads of the data object, because even if the object is stored in a decomposed and indexed fashion, the query or queries necessary to retrieve the object can simply include a filter similar to                VERSION=[desired version]to retrieve the desired version of the data object.        
The disadvantage of the deep copy implementation of object versioning is that if the data object is large in relation to the size of the typical changes, a large amount of storage and index space must be devoted to redundant copies of the same information. Edits become slower because of the need to make a deep copy of the data object each time a change to the object is made. For example, consider a data object (e.g. an XML document) that stores an employee hierarchy, which may be both large and undergoing small revisions on a daily basis. Clearly, the deep copy implementation of object versioning is poorly suited to this situation because it would cause the creation and storage of numerous copies that include redundant information. There are two approaches that may be employed to address the disadvantages of the deep copy implementation of object versioning, both of which sacrifice some ability to edit older versions of the data object.
The first approach is to store the incremental changes (or “deltas”) that are made to a data object. A collection of such changes constitutes a new version of the data object. A specific version of the data object may be reconstructed by successively applying the changes to the object. While this approach may save storage space, typically it is very costly to reconstruct versions of the data object because the successive application of each version's changes is computationally and resource intensive. (This drawback may be mitigated by occasionally inserting a deep copy of the data object into the sequence of stored changes, which provides a somewhat acceptable performance for retrieving older versions of the object; however, if an edit to an older version is needed, a complex reworking of the sequences of stored changes and deep copies would be required.) Thus, this approach is poorly suited for supporting the storage of a data object in componentized fashion and for the modification and retrieval of object nodes on a per-node basis (e.g. for supporting the storage, modification, and retrieval of only a portion of an employee tree represented in an XML document).
The second approach is to separately version each node of the data object (e.g. to separately version each of the elements of an XML document that stores an employee tree, and to reassemble the document based on version filters.) This approach avoids the need to reconstruct the data object by processing a series of deltas. In this approach, the query or queries to retrieve a desired version of the data object would need to include a filter similar to                VERSION<=[desired version]for each node of the object. However, while this filter may eliminate older versions of object nodes, it still yields both the desired version of the data object and any newer versions of the object. It must therefore be accompanied by a costly post-processing phase to eliminate the newer versions of the object nodes before the retrieval of the desired version of the data object is completed. Further, this approach still has an editing drawback similar to that of the delta versioning approach. That is, while it is possible to retrieve an older version of the data object, the older versions of the object cannot be easily edited with whole-object semantics. The reason is that each version of an object node may be “supporting” (e.g. included into) multiple logical versions of the entire object. For example, a particular node may have not changed across multiple versions of the object, where the multiple versions of the object are caused by changes to other object nodes. In this example, changing the old version of the particular node would have the unintended effect of changing the newer multiple versions of the data object as well.        
Based on the foregoing, there is a clear need for techniques which support true whole-object semantics for editing and retrieval of versioned data object, but which overcome the disadvantages of the approaches described above.